package cz.muni.fi.pb138.xml.wrda.dao;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamImplicit;
import cz.muni.fi.pb138.xml.wrda.core.WAR;
import org.basex.core.BaseXException;
import org.basex.core.Context;
import org.basex.core.cmd.Open;
import org.basex.core.cmd.XQuery;

import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Created by Mychal on 5/8/14.
 */
public class WARDaoImpl implements WARDao {

    private Context context;
    private final XStream xStream = createXStream();

    private XStream createXStream() {
        XStream xStream = new XStream();
        xStream.alias("WarEntries", WarEntries.class);
        xStream.registerConverter(new WAR.DocumentConverter());
        xStream.autodetectAnnotations(true);
        return xStream;
    }

    public WARDaoImpl() {
        context = new Context();
        try {
            Properties p = new Properties();
            p.load(this.getClass().getResourceAsStream("/config.properties"));
            new Open(p.getProperty("db.war.name")).execute(context);
        } catch (BaseXException e) {
            Logger.getLogger(WARDaoImpl.class.getName()).log(Level.SEVERE, "Can't open XSD database by context.", e);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void save(WAR war) throws IllegalArgumentException {
        String query = MessageFormat.format("insert node {0} into {1}", war.toString(), "/" + WarEntries.class.getSimpleName());

        try {
            new XQuery(query).execute(context);
        } catch (BaseXException e) {
            Logger.getLogger(WARDaoImpl.class.getName()).log(Level.SEVERE, "Can't insert into WAR database by context.", e);
        }
    }

    @Override
    public void delete(WAR war) throws IllegalArgumentException {
        String query = "delete node /WarEntries/WAR[fileName = '" + war.getFileName() + "' and timeStamp = '" + war.getTimeStamp() + "']";
        try {
            new XQuery(query).execute(context);
        } catch (BaseXException e) {
            Logger.getLogger(WARDaoImpl.class.getName()).log(Level.SEVERE, "Can't delete from WAR database by context.", e);
        }
    }

    @Override
    public List<WAR> getAllEntries() {

        String result = null;
        try {
            result = new XQuery("/").execute(context);
        } catch (BaseXException e) {
            Logger.getLogger(WARDaoImpl.class.getName()).log(Level.SEVERE, "Can't get entries from WAR database by context.", e);
        }

        List<WAR> wars = new ArrayList<>();
        if (!result.isEmpty()) {
            WarEntries entries = (WarEntries) xStream.fromXML(result);
            if (entries == null || entries.getList() == null) return wars;
            wars.addAll(entries.getList());
            return wars;
        }
        return wars;
    }

    @Override
    public WAR getByFileNameAndTimeStamp(String fileName, Long timeStamp) throws IllegalArgumentException {
        String query = "/WarEntries/WAR[fileName = '" + fileName + "' and timeStamp = '" + timeStamp + "']";

        String result = "";
        try {
            result = new XQuery(query).execute(context);
        } catch (BaseXException e) {
            e.printStackTrace();
        }

        if (!result.isEmpty()) {
            return (WAR) xStream.fromXML(result);
        }
        return null;
    }

    @Override
    public List<WAR> getByFileName(String fileName) throws IllegalArgumentException {
        String query = "/WarEntries/WAR[contains(./fileName, '" + fileName + "')]";

        String result = "";
        try {
            result = new XQuery(query).execute(context);
        } catch (BaseXException e) {
            e.printStackTrace();
        }

        List<WAR> wars = new ArrayList<>();
        if (!result.isEmpty()) {
            result = "<WarEntries>" + result + "</WarEntries>";
            WarEntries entries = (WarEntries) xStream.fromXML(result);
            if (entries == null || entries.getList() == null) return wars;
            wars.addAll(entries.getList());
            return wars;
        }
        return wars;
    }

    @Override
    public List<WAR> getByListener(String listenerName) {
        String query = "/WarEntries/WAR[contains(listeners/listener, '" + listenerName + "')]";

        String result = "";
        try {
            result = new XQuery(query).execute(context);
        } catch (BaseXException e) {
            e.printStackTrace();
        }

        List<WAR> wars = new ArrayList<>();
        if (!result.isEmpty()) {
            result = "<WarEntries>" + result + "</WarEntries>";
            WarEntries entries = (WarEntries) xStream.fromXML(result);
            if (entries == null || entries.getList() == null) return wars;
            wars.addAll(entries.getList());
            return wars;
        }
        return wars;
    }

    @Override
    public List<WAR> getByFilter(String filterName) {
        String query = "/WarEntries/WAR[contains(filters/filter, '" + filterName + "')]";

        String result = "";
        try {
            result = new XQuery(query).execute(context);
        } catch (BaseXException e) {
            e.printStackTrace();
        }

        List<WAR> wars = new ArrayList<>();
        if (!result.isEmpty()) {
            result = "<WarEntries>" + result + "</WarEntries>";
            WarEntries entries = (WarEntries) xStream.fromXML(result);
            if (entries == null || entries.getList() == null) return wars;
            wars.addAll(entries.getList());
            return wars;
        }
        return wars;
    }

    @Override
    public List<String> getFileNames() {
        String query = "distinct-values(/WarEntries/WAR/fileName)";


        String result = "";
        try {
            result = new XQuery(query).execute(context);
        } catch (BaseXException e) {
            e.printStackTrace();
        }

        List<String> fileNames = new ArrayList<>();
        if (!result.isEmpty()) {
            for (String s : result.split(" ")) {
                fileNames.add(s);
            }
        }
        return fileNames;
    }


    private class WarEntries {

        @XStreamImplicit(itemFieldName = "WAR")
        List<WAR> entries = new ArrayList<>();

        public void setList(List<WAR> list) {
            this.entries = list;
        }

        public List<WAR> getList() {
            return entries;
        }
    }


    /**
     * For testing purpose
     */
    public static void main(String[] args) throws IOException, InterruptedException {


        //testing
//        Context con = new Context();
//        Properties p = new Properties();
//        p.load(WARDaoImpl.class.getResourceAsStream("/config.properties"));
//        new DropDB(p.getProperty("db.war.name"));
//        new CreateDB(p.getProperty("db.war.name"), "../WRDA_data_layer/src/main/resources/wardb.xml").execute(con);
//        con.close();
//
//
        WAR test = new WAR();
        test.setFileName("test");
        test.getFilters().add("filter");
        test.getListeners().add("listener");
//
//        Thread.sleep(20);
//        WAR test2 = new WAR();
//        test2.setFileName("test2");
//        test2.getFilters().add("filter2");
//        test2.getFilters().add("filter3");
//        test2.getListeners().add("listener2");
//        test2.setId(2L);
//
//
        WARDao dao = new WARDaoImpl();
//
        dao.save(test);
//        dao.save(test2);
//
//        WAR test3 = new WAR();
//        test3.setId(3L);
//        test3.setFileName("test3");
//
//        dao.save(test3);
//
//        System.out.println(dao.getAllEntries());
//        System.out.println();
//        System.out.println(dao.getByFileName("testasd"));
//        System.out.println();
//        System.out.println(dao.getByListener("listener2"));
//        System.out.println();
//        System.out.println(dao.getByFilter("filter2"));
//        System.out.println();
//        System.out.println(dao.getById(4L));
    }
}
